package servlet;

import config.Action;
import config.ActionMapping;
import config.Forward;
import config.Test;
import exception.ActionNotFoundException;
import exception.ConfigErrorException;
import exception.ForwardNotFoundException;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.xml.sax.SAXException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 石小俊
 */
@WebServlet(urlPatterns = "*.do", loadOnStartup = 1, initParams = {
        @WebInitParam(name = "configLocation", value = "actions.xml")
})
public class ActionServlet extends HttpServlet {

    private ActionMapping actionMapping;
    private Map<String, Object> objs;

    @Override
    public void init() throws ServletException {
        objs = new HashMap<>();
        Digester digester = DigesterLoader.createDigester(Test.class.getClassLoader().getResource("config/rule.xml"));
        actionMapping = new ActionMapping();
        digester.push(actionMapping);
        try {
            String configLocation = getInitParameter("configLocation");
            digester.parse(Test.class.getClassLoader().getResourceAsStream(configLocation));
        } catch (IOException e) {
            e.printStackTrace();
            throw new ConfigErrorException("解析配置文件出错");
        } catch (SAXException e) {
            e.printStackTrace();
            throw new ConfigErrorException("解析配置文件出错");
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(actionMapping);
        // 获取请求,配置文件中没有配置.do,将获取到的请求的后缀截取掉
        String path = request.getServletPath();
        path = path.substring(0, path.lastIndexOf("."));

        // 1.去配置文件中找到对应请求的配置所在的Action配置,并创建type对应的对象
        Action action = actionMapping.findAction(path);
        System.out.println("action:" + action);
        // 如果没有找到对应的Action,说明没有对应请求的配置,抛出异常
        if (action == null) {
            throw new ActionNotFoundException("请求" + path + "在配置文件中尚未配置");
        }

        try {
            // 根据Action中type的值,创建对应的action对象
            // 注意:由于多个配置中的type值可能相同,在创建对象时如果每次都创建新对象效率较低
            // 因此,我们在创建对象的时候要求每个对象只创建一次
            // 根据type的值在Map集合中获取对应对象
            Class actionClass = Class.forName(action.getType());
            Object obj = objs.get(action.getType());
            if (obj == null) {
                // 如果没有找到对应对象,说明尚未创建
                // 创建一个对象
                obj = actionClass.newInstance();
                // 将创建的对象存放到集合中
                objs.put(action.getType(), obj);
            }

            // 2.根据配置找到对应的Action中的方法
            // 可以通过Action配置的parameter属性找到对应的方法名
            // 根据反射去获取对应的Method,限制方法参数必须是HttpServletRequest与HttpServletResponse
            Method method = actionClass.getMethod(action.getParameter(), HttpServletRequest.class, HttpServletResponse.class);

            // 3.执行对应方法,获取到方法的返回值
            String result = (String) method.invoke(obj, request, response);
            System.out.println("方法返回值:" + result);
            if (result == null) {
                System.out.println("当前方法无返回值,请使用Ajax进行操作");
                return;
            }

            // 4.根据返回值找到对应的forward配置
            Forward forward = action.findForward(result);
            if (forward == null) {
                throw new ForwardNotFoundException("返回值" + result + "在配置文件中尚未配置");
            }

            // 5.根据forward配置中的redirect选择转发或者重定向到指定的path页面
            if (forward.getRedirect()) {
                // 如果值为true,则使用重定向跳转到path所对应的页面
                response.sendRedirect(request.getContextPath() + forward.getPath());
            } else {
                // 如果值为false,则使用转发跳转到path所对应的页面
                request.getRequestDispatcher(forward.getPath()).forward(request, response);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServletException(e.getMessage());
        }


    }

}
